home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Xconq 7.1.0 / src / xconq-7.1.0 / kernel / nlang.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-07  |  55.6 KB  |  2,388 lines  |  [TEXT/R*ch]

  1. /* Interface-independent natural language handling for Xconq.
  2.    Copyright (C) 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996
  3.    Stanley T. Shebs.
  4.  
  5. Xconq is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.  See the file COPYING.  */
  9.  
  10. /* This file should be entirely replaced for non-English Xconq. */
  11. /* (One way to do this would be to call this file "nlang-en.c", then
  12.    symlink this or nlang-fr.c, etc to nlang.c when configuring;
  13.    similarly for help.c.) */
  14. /* (A better way to would be to run all these through a dispatch
  15.    vector, then each player could get messages and such in a
  16.    different language.) */
  17.  
  18. #include "conq.h"
  19. extern void location_desc PARAMS ((char *buf, Side *side, Unit *unit, int u, int x, int y));
  20. extern int supply_desc PARAMS ((char *buf, Unit *unit, int mrow));
  21.  
  22. static void init_calendar PARAMS ((void));
  23. static void maybe_mention_date PARAMS ((Side *side));
  24.  
  25. static int gain_count PARAMS ((Side *side, int u, int r));
  26. static int loss_count PARAMS ((Side *side, int u, int r));
  27. static int atkstats PARAMS ((Side *side, int a, int d));
  28. static int hitstats PARAMS ((Side *side, int a, int d));
  29.  
  30. static char *tmpnbuf;
  31.  
  32. static char *tmpdbuf;
  33.  
  34. static char *pluralbuf;
  35.  
  36. /* Short names of directions. */
  37.  
  38. char *dirnames[] = DIRNAMES;
  39.  
  40. char *unitbuf = NULL;
  41.  
  42. char *past_unitbuf = NULL;
  43.  
  44. static char *side_short_title = NULL;
  45.  
  46. static char *gain_reason_names[] = { "Ini", "Bld", "Cap", "Oth" };
  47.  
  48. static char *loss_reason_names[] = { "Cbt", "Cap", "Stv", "Acc", "Dis", "Oth" };
  49.  
  50. /* Calendar handling. */
  51.  
  52. typedef enum {
  53.     cal_unknown,
  54.     cal_turn,
  55.     cal_usual
  56. } CalendarType;
  57.  
  58. static CalendarType calendar_type = cal_unknown;
  59.  
  60. typedef enum {
  61.     ds_second,
  62.     ds_minute,
  63.     ds_hour,
  64.     ds_day,
  65.     ds_week,
  66.     ds_month,
  67.     ds_season,
  68.     ds_year
  69. } UsualDateStepType;
  70.  
  71. typedef struct a_usualdate {
  72.     int second;
  73.     int minute;
  74.     int hour;
  75.     int day;
  76.     int month;
  77.     int year;
  78. } UsualDate;
  79.  
  80. static char *usual_date_string PARAMS ((int date));
  81. static void parse_usual_date PARAMS ((char *datestr, UsualDate *udate));
  82.  
  83. static UsualDateStepType datesteptype;
  84.  
  85. static int datestep;
  86.  
  87. static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  88.            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???" };
  89.  
  90. UsualDate *usual_initial;
  91.  
  92. char *datebuf;
  93.  
  94. char *turnname;
  95.  
  96. /* This is the number of types to mention by name; any others will
  97.    just be included in the count of missing images. */
  98.  
  99. #define NUMTOLIST 5
  100.  
  101. static char *missinglist = NULL;
  102.  
  103. /* This array allows for counting up to 4 classes of missing images. */
  104.  
  105. static int missing[4];
  106.  
  107. static int totlisted = 0;
  108.  
  109. /* Initialize things.  Note that this happens before any game is loaded, so
  110.    can't do game-specific init here. */
  111.  
  112. void
  113. init_nlang()
  114. {
  115.     if (tmpnbuf == NULL)
  116.       tmpnbuf = xmalloc(BUFSIZE);
  117.     if (tmpdbuf == NULL)
  118.       tmpdbuf = xmalloc(BUFSIZE);
  119.     if (pluralbuf == NULL)
  120.       pluralbuf = xmalloc(BUFSIZE);
  121.     if (datebuf == NULL)
  122.       datebuf = xmalloc(BUFSIZE);
  123. }
  124.  
  125. /* Send a message to everybody who's got a screen. */
  126.  
  127. #ifdef __STDC__
  128. void
  129. notify_all(char *fmt, ...)
  130. #else
  131. void
  132. notify_all(fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
  133. char *fmt;
  134. long a1, a2, a3, a4, a5, a6, a7, a8, a9;
  135. #endif
  136. {
  137.     Side *side;
  138.  
  139.     for_all_sides(side) {
  140.     if (active_display(side)) {
  141.         maybe_mention_date(side);
  142. #ifdef __STDC__
  143.         {
  144.         va_list ap;
  145.  
  146.         va_start(ap, fmt);
  147.         vsprintf(tmpnbuf, fmt, ap);
  148.         va_end(ap);
  149.         }
  150. #else
  151.         sprintf(tmpnbuf, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
  152. #endif
  153.         /* Always capitalize first char of notice. */
  154.         if (islower(tmpnbuf[0]))
  155.           tmpnbuf[0] = toupper(tmpnbuf[0]);
  156.         low_notify(side, tmpnbuf);
  157.     }
  158.     }
  159. }
  160.  
  161. #ifdef __STDC__
  162. void
  163. notify(Side *side, char *fmt, ...)
  164. #else
  165. void
  166. notify(side, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
  167. Side *side;
  168. char *fmt;
  169. long a1, a2, a3, a4, a5, a6, a7, a8, a9;
  170. #endif
  171. {
  172.     if (!active_display(side))
  173.       return;
  174.     maybe_mention_date(side);
  175. #ifdef __STDC__
  176.     {
  177.     va_list ap;
  178.  
  179.     va_start(ap, fmt);
  180.     vsprintf(tmpnbuf, fmt, ap);
  181.     va_end(ap);
  182.     }
  183. #else
  184.     sprintf(tmpnbuf, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
  185. #endif
  186.     /* Always capitalize first char of notice. */
  187.     if (islower(tmpnbuf[0]))
  188.       tmpnbuf[0] = toupper(tmpnbuf[0]);
  189.     low_notify(side, tmpnbuf);
  190. }
  191.  
  192. #if defined(__STDC__) || defined(MPW_C)
  193. void
  194. vnotify(side, fmt, ap)
  195. Side *side;
  196. char *fmt;
  197. va_list ap;
  198. {
  199.     if (!active_display(side))
  200.       return;
  201.     maybe_mention_date(side);
  202.     vsprintf(tmpnbuf, fmt, ap);
  203.     /* Always capitalize first char of notice. */
  204.     if (islower(tmpnbuf[0]))
  205.       tmpnbuf[0] = toupper(tmpnbuf[0]);
  206.     low_notify(side, tmpnbuf);
  207. }
  208. #endif /* __STDC__ */
  209.  
  210. static void
  211. maybe_mention_date(side)
  212. Side *side;
  213. {
  214.     /* Note that last_notice_date defaults to 0, so this means that
  215.        any turn 0 notices will not have a date prepended (which is good). */
  216.     if (g_turn() != side->last_notice_date) {
  217.     sprintf(tmpdbuf, "%s:", absolute_date_string(g_turn()));
  218.     low_notify(side, tmpdbuf);
  219.     side->last_notice_date = g_turn();
  220.     }
  221. }
  222.  
  223. /* Pad a given buffer with blanks out to the given position, and cut off any
  224.    additional content. */
  225.  
  226. void
  227. pad_out(buf, n)
  228. char *buf;
  229. int n;
  230. {
  231.     int i, len = strlen(buf);
  232.  
  233.     if (n < 1)
  234.       return;
  235.     for (i = len; i < n; ++i) {
  236.     buf[i] = ' ';
  237.     }
  238.     buf[n - 1] = '\0';
  239. }
  240.  
  241. /* Get a char string naming the side.  Doesn't have to be pretty. */
  242.  
  243. /* (should synth complete name/adjective from other parts of speech) */
  244.  
  245. char *
  246. side_name(side)
  247. Side *side;
  248. {
  249.     return (side == NULL ? "independent" :
  250.       (side->name ? side->name :
  251.         (side->adjective ? side->adjective :
  252.          (side->pluralnoun ? side->pluralnoun :
  253.           (side->noun ? side->noun :
  254.            "")))));
  255. }
  256.  
  257. char *
  258. side_adjective(side)
  259. Side *side;
  260. {
  261.     return (side == NULL ? "independent" :
  262.        (side->adjective ? side->adjective :
  263.         (side->noun ? side->noun :
  264.          (side->pluralnoun ? side->pluralnoun :
  265.           (side->name ? side->name :
  266.            "")))));
  267. }
  268.  
  269. char *
  270. short_side_title(side)
  271. Side *side;
  272. {
  273.     if (side_short_title == NULL)
  274.       side_short_title = xmalloc(BUFSIZE);
  275.     if (side == NULL) {
  276.     return " - ";
  277.     } else if (side->name) {
  278.     return side->name;
  279.     } else if (side->pluralnoun) {
  280.     sprintf(side_short_title, "the %s", side->pluralnoun);
  281.     } else if (side->noun) {
  282.     sprintf(side_short_title, "the %s", plural_form(side->noun));
  283.     } else if (side->adjective) {
  284.     sprintf(side_short_title, "the %s side", side->adjective);
  285.     } else {
  286.     return " - ";
  287.     }
  288.     return side_short_title;
  289. }
  290.  
  291. char *
  292. short_side_title_with_adjective(side, adjective)
  293. Side *side;
  294. char *adjective;
  295. {
  296.     if (side_short_title == NULL)
  297.       side_short_title = xmalloc(BUFSIZE);
  298.     side_short_title[0] = '\0';
  299.     if (side == NULL) {
  300.     return " - ";
  301.     } else if (side->name) {
  302.     if (empty_string(adjective))
  303.       return side->name;
  304.     else {
  305.         strcat(side_short_title, adjective);
  306.         strcat(side_short_title, " ");
  307.         strcat(side_short_title, side->name);
  308.     }
  309.     } else if (side->pluralnoun) {
  310.     strcat(side_short_title, "the ");
  311.     if (!empty_string(adjective)) {
  312.         strcat(side_short_title, adjective);
  313.         strcat(side_short_title, " ");
  314.     }
  315.     strcat(side_short_title, side->pluralnoun);
  316.     } else if (side->noun) {
  317.     strcat(side_short_title, "the ");
  318.     if (!empty_string(adjective)) {
  319.         strcat(side_short_title, adjective);
  320.         strcat(side_short_title, " ");
  321.     }
  322.     strcat(side_short_title, side->noun);
  323.     } else if (side->adjective) {
  324.     strcat(side_short_title, "the ");
  325.     if (!empty_string(adjective)) {
  326.         strcat(side_short_title, adjective);
  327.         strcat(side_short_title, " ");
  328.     }
  329.     strcat(side_short_title, side->adjective);
  330.     strcat(side_short_title, " side");
  331.     } else {
  332.     return " - ";
  333.     }
  334.     return side_short_title;
  335. }
  336.  
  337. /* This indicates whether the above routine returns a singular or plural form
  338.    of title. */
  339.  
  340. int
  341. short_side_title_plural_p(side)
  342. Side *side;
  343. {
  344.     if (side == NULL) {
  345.     return FALSE;
  346.     } else if (side->name) {
  347.     return FALSE;
  348.     } else if (side->pluralnoun) {
  349.     return TRUE;
  350.     } else if (side->noun) {
  351.     return TRUE;
  352.     } else if (side->adjective) {
  353.     sprintf(side_short_title, "The %s side", side->adjective);
  354.     return FALSE;
  355.     } else {
  356.     return FALSE;
  357.     }
  358. }
  359.  
  360. char *
  361. shortest_side_title(side2, buf)
  362. Side *side2;
  363. char *buf;
  364. {
  365.     if (side2 == NULL) {
  366.     return "-";
  367.     } else if (side2->name) {
  368.     return side2->name;
  369.     } else if (side2->adjective) {
  370.     return side2->adjective;
  371.     } else if (side2->noun) {
  372.     return side2->noun;
  373.     } else {
  374.     sprintf(buf, "(#%d)", side_number(side2));
  375.     }
  376.     return buf;
  377. }
  378.  
  379. /* (should let NULL observer be assumed objective observer) */
  380.  
  381. char *
  382. rel_short_side_title(side, side2, n)
  383. Side *side, *side2;
  384. int n;
  385. {
  386.     return "???";
  387. }
  388.  
  389. char *
  390. rel_long_side_title(side, side2)
  391. Side *side, *side2;
  392. {
  393.     return "???";
  394. }
  395.  
  396. char *
  397. narrative_side_desc(side, side2)
  398. Side *side, *side2;
  399. {
  400.     return "???";
  401. }
  402.  
  403. char *
  404. narrative_side_desc_first(side, side2)
  405. Side *side, *side2;
  406. {
  407.     return "???";
  408. }
  409.  
  410. char *
  411. side_score_desc(buf, side, sk)
  412. char *buf;
  413. Side *side;
  414. Scorekeeper *sk;
  415. {
  416.     if (symbolp(sk->body)
  417.         && match_keyword(sk->body, K_LAST_SIDE_WINS)) {
  418.     sprintf(buf, "Point Value: %d", side_point_value(side));
  419.     } else {
  420.     /* Compose the generic scorekeeper status display. */
  421.     if (sk->title != NULL) {
  422.         sprintf(buf, "%s", sk->title);
  423.     } else {
  424.         sprintf(buf, "SK #%d", sk->id);
  425.     }
  426.     if (sk->scorenum >= 0) {
  427.         tprintf(buf, ": %d", side->scores[sk->scorenum]);
  428.     }
  429.     }
  430.     return buf;
  431. }
  432.  
  433. char *
  434. long_player_title(buf, player, thisdisplayname)
  435. char *buf, *thisdisplayname;
  436. Player *player;
  437. {
  438.     buf[0] = '\0';
  439.     if (player == NULL) {
  440.     /* Do nothing */
  441.     } else if (player->displayname != NULL) {
  442.     if (player->name != NULL) {
  443.         strcat(buf, player->name);
  444.         strcat(buf, "@");
  445.     }
  446.     if (thisdisplayname != NULL
  447.         && strcmp(player->displayname, thisdisplayname) == 0) {
  448.         strcat(buf, "You");
  449.     } else {
  450.         strcat(buf, player->displayname);
  451.     }
  452.     if (player->aitypename != NULL) {
  453.         strcat(buf, "(& AI ");
  454.         strcat(buf, player->aitypename);
  455.         strcat(buf, ")");
  456.     }
  457.     } else if (player->aitypename != NULL) {
  458.     strcat(buf, "AI ");
  459.     strcat(buf, player->aitypename);
  460.     } else {
  461.     strcat(buf, "-");
  462.     }
  463.     return buf;
  464. }
  465.  
  466. char *
  467. short_player_title(buf, player, thisdisplayname)
  468. char *buf, *thisdisplayname;
  469. Player *player;
  470. {
  471.     buf[0] = '\0';
  472.     if (player == NULL)
  473.       return buf;
  474.     if (player->name != NULL) {
  475.     strcat(buf, player->name);
  476.     }
  477.     if (player->aitypename != NULL) {
  478.     strcat(buf, ",");
  479.     strcat(buf, player->aitypename);
  480.     }
  481.     if ((player->name != NULL || player->aitypename != NULL)
  482.     && player->displayname != NULL) {
  483.     strcat(buf, "@");
  484.     }
  485.     if (thisdisplayname != NULL
  486.     && player->displayname != NULL
  487.     && strcmp(player->displayname, thisdisplayname) == 0) {
  488.     strcat(buf, "You");
  489.     } else if (player->displayname != NULL) {
  490.     strcat(buf, player->displayname);
  491.     }
  492.     if (strlen(buf) == 0) {
  493.     strcat(buf, "-");
  494.     }
  495.     return buf;
  496. }
  497.  
  498. void
  499. side_and_type_name(buf, side, u, side2)
  500. char *buf;
  501. Side *side, *side2;
  502. int u;
  503. {
  504.     /* Decide how to identify the side. */
  505.     if (side2 == NULL) {
  506.     sprintf(buf, "independent ");
  507.     } else if (side == side2) {
  508.     sprintf(buf, "your ");
  509.     } else {
  510.     /* (this could be more elaborate) */
  511.     sprintf(buf, "%s ",
  512.         (side2->adjective ? side2->adjective :
  513.          (side2->noun ? side2->noun : "?")));
  514.     }
  515.     /* Glue the pieces together and return it. */
  516.     strcat(buf, u_type_name(u));
  517. }
  518.  
  519. /* Build a short phrase describing a given unit to a given side,
  520.    basically consisting of indication of unit's side, then of unit itself. */
  521.  
  522. char *
  523. unit_handle(side, unit)
  524. Side *side;
  525. Unit *unit;
  526. {
  527.     char *utypename;
  528.     Side *side2, *side3;
  529.  
  530.     if (unitbuf == NULL)
  531.       unitbuf = xmalloc(BUFSIZE);
  532.     /* Handle various weird situations. */
  533.     if (unit == NULL)
  534.       return "???";
  535.     if (!alive(unit)) {
  536.         sprintf(unitbuf, "dead #%d", unit->id);
  537.         return unitbuf;
  538.     }
  539.     /* If this unit represents "yourself", say so. */
  540.     if (side != NULL && unit == side->self_unit)
  541.       return "you";
  542. #if 0 /* how to do these days? */
  543.     /* Sometimes a unit's description should be its name alone. */
  544.     if (u_name_format(unit->type) == 2 && unit->name)
  545.       return unit->name;
  546. #endif
  547.     /* Decide how to identify the side.  If the unit's original side
  548.        is not its current side, list both of them. */
  549.     side2 = unit->side;
  550.     side3 = NULL;
  551.     if (unit->origside != NULL && side2 != unit->origside) {
  552.     side2 = unit->origside;
  553.     side3 = unit->side;
  554.     }
  555.     if (side2 == NULL) {
  556.     sprintf(unitbuf, "the ");
  557.     } else if (side2 == side) {
  558.     sprintf(unitbuf, "your ");
  559.     } else {
  560.     sprintf(unitbuf, "the %s ",
  561.         (side2->adjective ? side2->adjective :
  562.          (side2->noun ? side2->noun : side_desig(side2))));
  563.     }
  564.     if (side3 != NULL) {
  565.     if (side3 == side) {
  566.         tprintf(unitbuf, "(your) ");
  567.     } else {
  568.         tprintf(unitbuf, "(%s-held) ",
  569.             (side3->adjective ? side3->adjective :
  570.              (side3->noun ? side3->noun : side_desig(side3))));
  571.     }
  572.     }
  573.     /* Now add the unit's unique description. */
  574.     /* (Should this ever use short name?) */
  575.     utypename = u_type_name(unit->type);
  576.     if (unit->name) {
  577.     tprintf(unitbuf, "%s %s", utypename, unit->name);
  578.     } else if (unit->number > 0) {
  579.     tprintf(unitbuf, "%d%s %s",
  580.         unit->number, ordinal_suffix(unit->number), utypename);
  581.     } else {
  582.     tprintf(unitbuf, "%s", utypename);
  583.     }
  584.     return unitbuf;
  585. }
  586.  
  587. /* Shorter unit description omits side name, but uses same buffer. */
  588.  
  589. char *
  590. short_unit_handle(unit)
  591. Unit *unit;
  592. {
  593.     int u;
  594.  
  595.     if (unitbuf == NULL)
  596.       unitbuf = xmalloc(BUFSIZE);
  597.     if (unit == NULL)
  598.       return "???";
  599.     if (!alive(unit)) {
  600.         sprintf(unitbuf, "dead #%d", unit->id);
  601.         return unitbuf;
  602.     }
  603.     u = unit->type;
  604.     if (!empty_string(unit->name)) {
  605.     sprintf(unitbuf, "%s", unit->name);
  606.     } else if (!empty_string(u_short_name(u))) {
  607.     sprintf(unitbuf, "%ld%s %s",
  608.         unit->number, ordinal_suffix(unit->number), u_short_name(u));
  609.     } else {
  610.     sprintf(unitbuf, "%ld%s %s",
  611.         unit->number, ordinal_suffix(unit->number), u_type_name(u));
  612.     }
  613.     return unitbuf;
  614. }
  615.  
  616. void
  617. name_or_number(unit, buf)
  618. Unit *unit;
  619. char *buf;
  620. {
  621.     if (unit->name) {
  622.     sprintf(buf, "%s", unit->name);
  623.     } else if (unit->number > 0) {
  624.     sprintf(buf, "%ld%s", unit->number, ordinal_suffix(unit->number));
  625.     } else {
  626.     buf[0] = '\0';
  627.     }
  628. }
  629.  
  630. /* Build a short phrase describing a given past unit to a given side,
  631.    basically consisting of indication of unit's side, then of unit itself. */
  632.  
  633. char *
  634. past_unit_handle(side, past_unit)
  635. Side *side;
  636. PastUnit *past_unit;
  637. {
  638.     char *utypename;
  639.     Side *side2;
  640.  
  641.     if (past_unitbuf == NULL)
  642.       past_unitbuf = xmalloc(BUFSIZE);
  643.     /* Handle various weird situations. */
  644.     if (past_unit == NULL)
  645.       return "???";
  646.     /* Decide how to identify the side. */
  647.     side2 = past_unit->side;
  648.     if (side2 == NULL) {
  649.     sprintf(past_unitbuf, "the ");
  650.     } else if (side2 == side) {
  651.     sprintf(past_unitbuf, "your ");
  652.     } else {
  653.     sprintf(past_unitbuf, "the %s ",
  654.         (side2->adjective ? side2->adjective :
  655.          (side2->noun ? side2->noun : side_desig(side2))));
  656.     }
  657.     /* Now add the past_unit's unique description. */
  658.     /* (Should this ever use short name?) */
  659.     utypename = u_type_name(past_unit->type);
  660.     if (past_unit->name) {
  661.     tprintf(past_unitbuf, "%s %s", utypename, past_unit->name);
  662.     } else if (past_unit->number > 0) {
  663.     tprintf(past_unitbuf, "%ld%s %s",
  664.         past_unit->number, ordinal_suffix(past_unit->number), utypename);
  665.     } else {
  666.     tprintf(past_unitbuf, "%s", utypename);
  667.     }
  668.     return past_unitbuf;
  669. }
  670.  
  671. /* Shorter past_unit description omits side name, but uses same buffer. */
  672.  
  673. char *
  674. short_past_unit_handle(past_unit)
  675. PastUnit *past_unit;
  676. {
  677.     int u;
  678.  
  679.     if (past_unitbuf == NULL)
  680.       past_unitbuf = xmalloc(BUFSIZE);
  681.     if (past_unit == NULL)
  682.       return "???";
  683.     u = past_unit->type;
  684.     if (!empty_string(past_unit->name)) {
  685.     sprintf(past_unitbuf, "%s", past_unit->name);
  686.     } else if (!empty_string(u_short_name(u))) {
  687.     sprintf(past_unitbuf, "%ld%s %s",
  688.         past_unit->number, ordinal_suffix(past_unit->number), u_short_name(u));
  689.     } else {
  690.     sprintf(past_unitbuf, "%ld%s %s",
  691.         past_unit->number, ordinal_suffix(past_unit->number), u_type_name(u));
  692.     }
  693.     return past_unitbuf;
  694. }
  695.  
  696. void
  697. past_name_or_number(past_unit, buf)
  698. PastUnit *past_unit;
  699. char *buf;
  700. {
  701.     if (past_unit->name) {
  702.     sprintf(buf, "%s", past_unit->name);
  703.     } else if (past_unit->number > 0) {
  704.     sprintf(buf, "%ld%s", past_unit->number, ordinal_suffix(past_unit->number));
  705.     } else {
  706.     buf[0] = '\0';
  707.     }
  708. }
  709.  
  710. /* Given a unit and optional type u, summarize construction status
  711.    and timing. */
  712. /* (should reindent) */
  713. void
  714. construction_desc(buf, unit, u)
  715. char *buf;
  716. Unit *unit;
  717. int u;
  718. {
  719.     int est, u2;
  720.     char ubuf[10], tmpbuf[100];
  721.     Task *task;
  722.     Unit *unit2;
  723.  
  724.     if (u != NONUTYPE) {
  725.     est = est_completion_time(unit, u);
  726.     if (est >= 0) {
  727.         sprintf(ubuf, "[%2d] ", est);
  728.     } else {
  729.         strcpy(ubuf, " --  ");
  730.     }
  731.     } else {
  732.     ubuf[0] = '\0';
  733.     }
  734.     name_or_number(unit, tmpbuf);
  735.     sprintf(buf, "%s%s %s", ubuf, u_type_name(unit->type), tmpbuf);
  736.     pad_out(buf, 25);
  737.     if (unit->plan
  738.     && unit->plan->tasks) {
  739.       task = unit->plan->tasks;
  740.       if (task->type == TASK_BUILD) {
  741.     u2 = task->args[0];
  742.     tprintf(buf, " %s ", (is_unit_type(u2) ? u_type_name(u2) : "?"));
  743.     unit2 = find_unit(task->args[1]);
  744.     if (in_play(unit2) && unit2->type == u2) {
  745.         tprintf(buf, "%d/%d done ", unit2->cp, u_cp(unit2->type));
  746.     }
  747.     tprintf(buf, "(%d of %d)", task->args[2] + 1, task->args[3]);
  748.       } else if (task->type == TASK_RESEARCH) {
  749.     u2 = task->args[0];
  750.         if (is_unit_type(u2)) {
  751.         tprintf(buf, " %s tech %d/%d",
  752.             u_type_name(u2), unit->side->tech[u2], task->args[1]);
  753.         }
  754.       }
  755.     }
  756. }
  757.  
  758. void
  759. constructible_desc(buf, side, u, unit)
  760. char *buf;
  761. Side *side;
  762. int u;
  763. Unit *unit;
  764. {
  765.     char estbuf[20];
  766.     char techbuf[50];
  767.     char typenamebuf[50];
  768.     int est, tp, num;
  769.  
  770.     if (unit != NULL) {
  771.     est = est_completion_time(unit, u);
  772.         if (est >= 0) {
  773.         sprintf(estbuf, "[%2d] ", est);
  774.         if (uu_tp_to_build(unit->type, u) > 0) {
  775.         tp = (unit->tooling ? unit->tooling[u] : 0);
  776.         tprintf(estbuf, "(%2d) ", tp);
  777.         }
  778.         } else {
  779.         strcpy(estbuf, " --  ");
  780.         }
  781.     } else {
  782.     estbuf[0] = '\0';
  783.     }
  784.     if (u_tech_max(u) > 0) {
  785.         sprintf(techbuf, "[T %d/%d/%d] ", side->tech[u], u_tech_to_build(u), u_tech_max(u));
  786.     } else {
  787.     techbuf[0] = '\0';
  788.     }
  789.     strcpy(typenamebuf, u_type_name(u));
  790.     /* If the single char for the type is different from the first character
  791.        of its type name, mention the char. */
  792.     if (!empty_string(u_uchar(u)) && (u_uchar(u))[0] != typenamebuf[0]) {
  793.     tprintf(typenamebuf, "(%c)", (u_uchar(u))[0]);
  794.     }
  795.     sprintf(buf, "%s%s%-16.16s", estbuf, techbuf, typenamebuf);
  796.     num = num_units_in_play(side, u);
  797.     if (num > 0) {
  798.     tprintf(buf, "  %3d", num);
  799.     }
  800.     num = num_units_incomplete(side, u);
  801.     if (num > 0) {
  802.     tprintf(buf, "(%d)", num);
  803.     }
  804. }
  805.  
  806. int
  807. est_completion_time(unit, u2)
  808. Unit *unit;
  809. int u2;
  810. {
  811.     int u, tooluptime, tp;
  812.  
  813.     u = unit->type;
  814.     if (uu_acp_to_create(u, u2) < 1)
  815.       return (-1);
  816.     tooluptime = 0;
  817.     tp = (unit->tooling ? unit->tooling[u2] : 0);
  818.     if (tp < uu_tp_to_build(u, u2)) {
  819.     if (uu_acp_to_toolup(u, u2) < 1 || uu_tp_per_toolup(u, u2) <= 0 || u_acp(u) <= 0)
  820.       return (-1);
  821.     tooluptime = ((uu_tp_to_build(u, u2) - tp) * uu_acp_to_toolup(u, u2))
  822.      / (uu_tp_per_toolup(u, u2) * u_acp(u));
  823.     }
  824.     return tooluptime + normal_completion_time(unit->type, u2);
  825. }
  826.  
  827. void
  828. historical_event_date_desc(hevt, buf)
  829. HistEvent *hevt;
  830. char *buf;
  831. {
  832.     sprintf(buf, "%d: ", hevt->startdate);
  833. }
  834.  
  835. int
  836. find_event_type(sym)
  837. Obj *sym;
  838. {
  839.     int i;
  840.  
  841.     for (i = 0; hevtdefns[i].name != NULL; ++i) {
  842.     if (strcmp(c_string(sym), hevtdefns[i].name) == 0)
  843.       return i;
  844.     }
  845.     return -1;
  846. }
  847.  
  848. void
  849. historical_event_desc(side, hevt, buf)
  850. Side *side;
  851. HistEvent *hevt;
  852. char *buf;
  853. {
  854.     int data0 = hevt->data[0];
  855.     int data1 = hevt->data[1];
  856.     Obj *rest, *head, *pattern, *text;
  857.     Unit *unit;
  858.     PastUnit *pastunit, *pastunit2;
  859.     Side *side2;
  860.     
  861.     for_all_list(g_event_messages(), rest) {
  862.     head = car(rest);
  863.     if (consp(head)) {
  864.         pattern = car(head);
  865.         if (symbolp(pattern)
  866.         && find_event_type(pattern) == hevt->type) {
  867.         text = cadr(head);
  868.         sprintf(buf, c_string(text));
  869.         return;
  870.         } else if (consp(pattern)
  871.                && symbolp(car(pattern))
  872.                && find_event_type(car(pattern)) == hevt->type
  873.                /* && args match pattern args */
  874.                ) {
  875.         text = cadr(head);
  876.         sprintf(buf, c_string(text));
  877.         return;
  878.         }
  879.     }
  880.     }
  881.     /* Generate a default description of the event. */
  882.     switch (hevt->type) {
  883.       case H_LOG_STARTED:
  884.     sprintf(buf, "we started recording events");
  885.     break;
  886.       case H_LOG_ENDED:
  887.     sprintf(buf, "we stopped recording events");
  888.     break;
  889.       case H_GAME_STARTED:
  890.     sprintf(buf, "we started the game");
  891.     break;
  892.       case H_GAME_SAVED:
  893.     sprintf(buf, "we saved the game");
  894.     break;
  895.       case H_GAME_RESTARTED:
  896.     sprintf(buf, "we restarted the game");
  897.     break;
  898.       case H_GAME_ENDED:
  899.     sprintf(buf, "we ended the game");
  900.     break;
  901.       case H_SIDE_JOINED:
  902.           side2 = side_n(data0);
  903.     sprintf(buf, "%s joined the game",
  904.         (side == side2 ? "you" : side_name(side2)));
  905.     break;
  906.       case H_SIDE_LOST:
  907.           side2 = side_n(data0);
  908.     sprintf(buf, "%s lost!", (side == side2 ? "you" : side_name(side2)));
  909.     /* Include an explanation of the cause, if there is one. */
  910.     if (data1 == -1) {
  911.         tprintf(buf, " (resigned)");
  912.     } else if (data1 == -2) {
  913.         tprintf(buf, " (self-unit died)");
  914.     } else if (data1 > 0) {
  915.         tprintf(buf, " (scorekeeper %d)", data1);
  916.     } else {
  917.         tprintf(buf, " (don't know why)");
  918.     }
  919.     break;
  920.       case H_SIDE_WITHDREW:
  921.           side2 = side_n(data0);
  922.     sprintf(buf, "%s withdrew!", (side == side2 ? "you" : side_name(side2)));
  923.     break;
  924.       case H_SIDE_WON:
  925.           side2 = side_n(data0);
  926.     sprintf(buf, "%s won!", (side == side2 ? "you" : side_name(side2)));
  927.     /* Include an explanation of the cause, if there is one. */
  928.     if (data1 > 0) {
  929.         tprintf(buf, " (scorekeeper %d)", data1);
  930.     } else {
  931.         tprintf(buf, " (don't know why)");
  932.     }
  933.     break;
  934.       case H_UNIT_CREATED:
  935.           side2 = side_n(data0);
  936.     sprintf(buf, "%s created ",
  937.         (side == side2 ? "you" : side_name(side2)));
  938.     unit = find_unit(data1);
  939.     if (unit != NULL) {
  940.         tprintf(buf, "%s", unit_handle(side, unit));
  941.     } else {
  942.         pastunit = find_past_unit(data1);
  943.         if (pastunit != NULL) {
  944.         tprintf(buf, "%s", past_unit_handle(side, pastunit));
  945.         } else {
  946.         tprintf(buf, "%d??", data1);
  947.         }
  948.     }
  949.     break;
  950.       case H_UNIT_COMPLETED:
  951.           side2 = side_n(data0);
  952.     sprintf(buf, "%s completed ",
  953.         (side == side2 ? "you" : side_name(side2)));
  954.     unit = find_unit(data1);
  955.     if (unit != NULL) {
  956.         tprintf(buf, "%s", unit_handle(side, unit));
  957.     } else {
  958.         pastunit = find_past_unit(data1);
  959.         if (pastunit != NULL) {
  960.         tprintf(buf, "%s", past_unit_handle(side, pastunit));
  961.         } else {
  962.         tprintf(buf, "%d??", data1);
  963.         }
  964.     }
  965.     break;
  966.       case H_UNIT_DAMAGED:
  967.     unit = find_unit_dead_or_alive(data0);
  968.     if (unit != NULL) {
  969.         sprintf(buf, "%s", unit_handle(side, unit));
  970.     } else {
  971.          pastunit = find_past_unit(data0);
  972.         if (pastunit != NULL) {
  973.         sprintf(buf, "%s", past_unit_handle(side, pastunit));
  974.         } else {
  975.         sprintf(buf, "%d??", data0);
  976.         }
  977.     }
  978.     tprintf(buf, " damaged (%d -> %d hp)", data1, hevt->data[2]);
  979.     break;
  980.       case H_UNIT_CAPTURED:
  981.     buf[0] = '\0';
  982.     /* Note that the second optional value, if present, is the id
  983.        of the unit that did the capturing. */
  984.     unit = find_unit_dead_or_alive(data1);
  985.     if (unit != NULL) {
  986.         tprintf(buf, "%s", unit_handle(side, unit));
  987.     } else {
  988.          pastunit = find_past_unit(data1);
  989.         if (pastunit != NULL) {
  990.         tprintf(buf, "%s", past_unit_handle(side, pastunit));
  991.         } else if (data1 == 0) {
  992.         tprintf(buf, "somebody");
  993.         } else {
  994.         tprintf(buf, "%d??", data1);
  995.         }
  996.     }
  997.      tprintf(buf, " captured ");
  998.      /* Describe the unit that was captured. */
  999.     unit = find_unit_dead_or_alive(data0);
  1000.     if (unit != NULL) {
  1001.         tprintf(buf, "%s", unit_handle(side, unit));
  1002.     } else {
  1003.          pastunit = find_past_unit(data0);
  1004.         if (pastunit != NULL) {
  1005.         tprintf(buf, "%s", past_unit_handle(side, pastunit));
  1006.         } else {
  1007.         tprintf(buf, "%d??", data0);
  1008.         }
  1009.     }
  1010.     break;
  1011.       case H_UNIT_ACQUIRED:
  1012.     buf[0] = '\0';
  1013.     if (data1 >= 0) {
  1014.         side2 = side_n(data1);
  1015.         tprintf(buf, "%s", (side == side2 ? "you" : side_name(side2)));
  1016.     } else {
  1017.         tprintf(buf, "somebody");
  1018.     }
  1019.      tprintf(buf, " acquired ");
  1020.      /* Describe the unit that was acquired. */
  1021.     unit = find_unit_dead_or_alive(data0);
  1022.     if (unit != NULL) {
  1023.         tprintf(buf, "%s", unit_handle(side, unit));
  1024.     } else {
  1025.          pastunit = find_past_unit(data0);
  1026.         if (pastunit != NULL) {
  1027.         tprintf(buf, "%s", past_unit_handle(side, pastunit));
  1028.         } else {
  1029.         tprintf(buf, "%d??", data0);
  1030.         }
  1031.     }
  1032.     break;
  1033.       case H_UNIT_REVOLTED:
  1034.     buf[0] = '\0';
  1035.      /* Describe the unit that revolted. */
  1036.     unit = find_unit_dead_or_alive(data0);
  1037.     if (unit != NULL) {
  1038.         tprintf(buf, "%s", unit_handle(side, unit));
  1039.     } else {
  1040.          pastunit = find_past_unit(data0);
  1041.         if (pastunit != NULL) {
  1042.         tprintf(buf, "%s", past_unit_handle(side, pastunit));
  1043.         } else {
  1044.         tprintf(buf, "%d??", data0);
  1045.         }
  1046.     }
  1047.      tprintf(buf, " revolted");
  1048.      if (data1 >= 0) {
  1049.         side2 = side_n(data1);
  1050.         tprintf(buf, ", went over to %s", (side == side2 ? "you" : side_name(side2)));
  1051.     }
  1052.     break;
  1053.       case H_UNIT_KILLED:
  1054.       case H_UNIT_DIED_IN_ACCIDENT:
  1055.       case H_UNIT_DIED_FROM_TEMPERATURE:
  1056.     /* Obviously, the unit mentioned here can only be a past unit. */
  1057.     pastunit = find_past_unit(data0);
  1058.     if (pastunit != NULL) {
  1059.         sprintf(buf, "%s", past_unit_handle(side, pastunit));
  1060.     } else {
  1061.         sprintf(buf, "%d??", data0);
  1062.     }
  1063.     if (hevt->type == H_UNIT_KILLED)
  1064.       tprintf(buf, " was destroyed");
  1065.     else if (hevt->type == H_UNIT_DIED_IN_ACCIDENT)
  1066.       tprintf(buf, " died in an accident");
  1067.     else
  1068.       tprintf(buf, " died from excessive temperature");
  1069.     break;
  1070.       case H_UNIT_WRECKED:
  1071.       case H_UNIT_WRECKED_IN_ACCIDENT:
  1072.     pastunit = find_past_unit(data0);
  1073.     if (pastunit != NULL) {
  1074.         sprintf(buf, "%s", past_unit_handle(side, pastunit));
  1075.     } else {
  1076.         sprintf(buf, "%d??", data0);
  1077.     }
  1078.     if (hevt->type == H_UNIT_WRECKED)
  1079.       tprintf(buf, " was wrecked");
  1080.     else
  1081.       tprintf(buf, " was wrecked in an accident");
  1082.     break;
  1083.       case H_UNIT_VANISHED:
  1084.     pastunit = find_past_unit(data0);
  1085.     if (pastunit != NULL) {
  1086.         sprintf(buf, "%s", past_unit_handle(side, pastunit));
  1087.     } else {
  1088.         sprintf(buf, "%d??", data0);
  1089.     }
  1090.     tprintf(buf, " vanished");
  1091.     break;
  1092.       case H_UNIT_DISBANDED:
  1093.     pastunit = find_past_unit(data0);
  1094.     if (pastunit != NULL) {
  1095.         sprintf(buf, "%s", past_unit_handle(side, pastunit));
  1096.     } else {
  1097.         sprintf(buf, "%d??", data0);
  1098.     }
  1099.     tprintf(buf, " was disbanded");
  1100.     break;
  1101.       case H_UNIT_GARRISONED:
  1102.     pastunit = find_past_unit(data0);
  1103.     if (pastunit != NULL) {
  1104.         sprintf(buf, "%s", past_unit_handle(side, pastunit));
  1105.     } else {
  1106.         sprintf(buf, "%d??", data0);
  1107.     }
  1108.     tprintf(buf, " was used to garrison ");
  1109.     unit = find_unit(data1);
  1110.     if (unit != NULL) {
  1111.         tprintf(buf, "%s", unit_handle(side, unit));
  1112.     } else {
  1113.         pastunit2 = find_past_unit(data1);
  1114.         if (pastunit2 != NULL) {
  1115.         tprintf(buf, "%s", past_unit_handle(side, pastunit2));
  1116.         } else {
  1117.         /* Should never happen, but don't choke if it does. */
  1118.         tprintf(buf, "?????");
  1119.         }
  1120.     }
  1121.     break;
  1122.       case H_UNIT_STARVED:
  1123.     pastunit = find_past_unit(data0);
  1124.     if (pastunit != NULL) {
  1125.         sprintf(buf, "%s", past_unit_handle(side, pastunit));
  1126.     } else {
  1127.         sprintf(buf, "%d??", data0);
  1128.     }
  1129.     tprintf(buf, " starved to death");
  1130.     break;
  1131.       case H_UNIT_LEFT_WORLD:
  1132.     pastunit = find_past_unit(data0);
  1133.     if (pastunit != NULL) {
  1134.         sprintf(buf, "%s", past_unit_handle(side, pastunit));
  1135.     } else {
  1136.         sprintf(buf, "%d??", data0);
  1137.     }
  1138.     tprintf(buf, " left the world");
  1139.     break;
  1140.       case H_UNIT_NAME_CHANGED:
  1141.     pastunit = find_past_unit(data0);
  1142.     if (pastunit != NULL) {
  1143.         sprintf(buf, "%s", past_unit_handle(side, pastunit));
  1144.     } else {
  1145.         sprintf(buf, "%d??", data0);
  1146.     }
  1147.     unit = find_unit(data1);
  1148.     if (unit != NULL) {
  1149.         if (unit->name != NULL)
  1150.           tprintf(buf, " changed name to \"%s\"", unit->name);
  1151.         else
  1152.           tprintf(buf, " became anonymous");
  1153.     } else {
  1154.         pastunit2 = find_past_unit(data1);
  1155.         if (pastunit2 != NULL) {
  1156.         if (pastunit2->name != NULL)
  1157.           tprintf(buf, " changed name to \"%s\"", pastunit2->name);
  1158.         else
  1159.           tprintf(buf, " became anonymous");
  1160.         }
  1161.     }
  1162.     break;
  1163.       default:
  1164.     /* Don't warn, will cause serious problems for windows that
  1165.        are lists of events, but make sure the non-understood event
  1166.        is obvious. */
  1167.     sprintf(buf, "?????????? \"%s\" ??????????",
  1168.         hevtdefns[hevt->type].name);
  1169.     break;
  1170.     }
  1171. }
  1172.  
  1173. char *
  1174. action_result_desc(rslt)
  1175. int rslt;
  1176. {
  1177.     char *str;
  1178.     
  1179.     switch (rslt) {
  1180.       case A_ANY_OK:
  1181.     str = "OK";
  1182.     break;
  1183.       case A_ANY_DONE:
  1184.     str = "done";
  1185.     break;
  1186.       case A_ANY_CANNOT_DO:
  1187.     str = "can never do";
  1188.     break;
  1189.       case A_ANY_NO_ACP:
  1190.     str = "insufficient acp";
  1191.     break;
  1192.       case A_ANY_NO_MATERIAL:
  1193.     str = "insufficient material";
  1194.     break;
  1195.       case A_ANY_TOO_FAR:
  1196.     str = "too far";
  1197.     break;
  1198.       case A_ANY_TOO_NEAR:
  1199.     str = "too near";
  1200.     break;
  1201.       case A_MOVE_NO_MP:
  1202.     str = "insufficient mp";
  1203.     break;
  1204.       case A_MOVE_CANNOT_LEAVE_WORLD:
  1205.     str = "cannot leave world";
  1206.     break;
  1207.       case A_MOVE_DEST_FULL:
  1208.     str = "destination full";
  1209.     break;
  1210.       case A_OVERRUN_FAILED:
  1211.     str = "overrun failed";
  1212.     break;
  1213.       case A_OVERRUN_SUCCEEDED:
  1214.     str = "overrun succeeded";
  1215.     break;
  1216.       case A_FIRE_INTO_OUTSIDE_WORLD:
  1217.     str = "cannot fire outside world";
  1218.     break;
  1219.       case A_CAPTURE_FAILED:
  1220.     str = "capture failed";
  1221.     break;
  1222.       case A_CAPTURE_SUCCEEDED:
  1223.     str = "capture succeeded";
  1224.     break;
  1225.       case A_ANY_ERROR:
  1226.     str = "misc error";
  1227.     break;
  1228.       default:
  1229.     str = "???";
  1230.     break;
  1231.     }
  1232.     return str;
  1233. }
  1234.  
  1235. /* Generate a description of the borders and connections in and around
  1236.    a location. */
  1237.  
  1238. void
  1239. linear_desc(buf, x, y)
  1240. char *buf;
  1241. int x, y;
  1242. {
  1243.     int t, dir;
  1244.  
  1245.     if (any_aux_terrain_defined()) {
  1246.     for_all_terrain_types(t) {
  1247.         if (t_is_border(t)
  1248.         && aux_terrain_defined(t)
  1249.         && any_borders_at(x, y, t)) {
  1250.         tprintf(buf, " %s", t_type_name(t)); 
  1251.         for_all_directions(dir) {
  1252.             if (border_at(x, y, dir, t)) {
  1253.             tprintf(buf, "/%s", dirnames[dir]);
  1254.             }
  1255.         }
  1256.         }
  1257.         if (t_is_connection(t)
  1258.         && aux_terrain_defined(t)
  1259.         && any_connections_at(x, y, t)) {
  1260.         tprintf(buf, " %s", t_type_name(t)); 
  1261.         for_all_directions(dir) {
  1262.             if (connection_at(x, y, dir, t)) {
  1263.             tprintf(buf, "/%s", dirnames[dir]);
  1264.             }
  1265.         }
  1266.         }
  1267.     }
  1268.     }
  1269. }
  1270.  
  1271. void
  1272. elevation_desc(buf, x, y)
  1273. char *buf;
  1274. int x, y;
  1275. {
  1276.     if (elevations_defined()) {
  1277.     sprintf(buf, "(Elev %d)", elev_at(x, y));
  1278.     }
  1279. }
  1280.  
  1281. char *
  1282. feature_desc(feature, buf)
  1283. Feature *feature;
  1284. char *buf;
  1285. {
  1286.     int i, capitalize = FALSE;
  1287.     char *str;
  1288.  
  1289.     if (feature == NULL)
  1290.       return NULL;
  1291.     if (feature->name) {
  1292.     /* Does the name need any substitutions done? */
  1293.     if (strchr(feature->name, '%')) {
  1294.         i = 0;
  1295.         for (str = feature->name; *str != '\0'; ++str) {
  1296.             if (*str == '%') {
  1297.             /* Interpret substitution directives. */
  1298.             switch (*(str + 1)) {
  1299.               case 'T':
  1300.             capitalize = TRUE;
  1301.               case 't':
  1302.             if (feature->typename) {
  1303.                 buf[i] = '\0';
  1304.                 strcat(buf, feature->typename);
  1305.                 if (capitalize && islower(buf[i]))
  1306.                   buf[i] = toupper(buf[i]);
  1307.                 i = strlen(buf);
  1308.             } else {
  1309.                 /* do nothing */
  1310.             }
  1311.             ++str;
  1312.             break;
  1313.               case '\0':
  1314.             break;  /* (should be error?) */
  1315.               default:
  1316.             /* (error?) */
  1317.             break;
  1318.             }
  1319.             } else {
  1320.             buf[i++] = *str;
  1321.             }
  1322.         }
  1323.         /* Close off the string. */
  1324.         buf[i] = '\0';
  1325.         return buf;
  1326.     } else {
  1327.         /* Return the name alone. */
  1328.         return feature->name;
  1329.     }
  1330.     } else {
  1331.     if (feature->typename) {
  1332.         sprintf(buf, "unnamed %s", feature->typename);
  1333.         return buf;
  1334.     }
  1335.     }
  1336.     /* No description of the location is available. */
  1337.     return NULL;
  1338. }
  1339.  
  1340. /* Generate a string describing what is at the given location. */
  1341.  
  1342. char *featurebuf = NULL;
  1343.  
  1344. char *
  1345. feature_name_at(x, y)
  1346. int x, y;
  1347. {
  1348.     int fid = (features_defined() ? raw_feature_at(x, y) : 0);
  1349.     Feature *feature;
  1350.  
  1351.     if (fid == 0)
  1352.       return NULL;
  1353.     feature = find_feature(fid);
  1354.     if (feature != NULL) {
  1355.     if (featurebuf == NULL)
  1356.       featurebuf = xmalloc(BUFSIZE);
  1357.     return feature_desc(feature, featurebuf);
  1358.     }
  1359.     /* No description of the location is available. */
  1360.     return NULL;
  1361. }
  1362.  
  1363. void
  1364. temperature_desc(buf, x, y)
  1365. char *buf;
  1366. int x, y;
  1367. {
  1368.     if (temperatures_defined()) {
  1369.     sprintf(buf, "(Temp %d)", temperature_at(x, y));
  1370.     }
  1371. }
  1372.  
  1373. #if 0
  1374.     int age, u;
  1375.     short view, prevview;
  1376.     Side *side2;
  1377.  
  1378.     /* Compose and display view history of this cell. */
  1379.     Dprintf("Drawing previous view info\n");
  1380.     age = side_view_age(side, curx, cury);
  1381.     prevview = side_prevview(side, curx, cury);
  1382.     if (age == 0) {
  1383.     if (prevview != view) {
  1384.         if (prevview == EMPTY) {
  1385.         /* misleading if prevview was set during init. */
  1386.         sprintf(tmpbuf, "Up to date; had been empty.");
  1387.         } else if (prevview == UNSEEN) {
  1388.         sprintf(tmpbuf, "Up to date; had been unexplored.");
  1389.         } else {
  1390.         side2 = side_n(vside(prevview));
  1391.         u = vtype(prevview);
  1392.         if (side2 != side) {
  1393.             sprintf(tmpbuf, "Up to date; had seen %s %s.",
  1394.                 (side2 == NULL ? "independent" :
  1395.                  side_name(side2)),
  1396.                 u_type_name(u));
  1397.         } else {
  1398.             sprintf(tmpbuf,
  1399.                 "Up to date; had been occupied by your %s.",
  1400.                 u_type_name(u));
  1401.         }
  1402.         }
  1403.     } else {
  1404.         sprintf(tmpbuf, "Up to date.");
  1405.     }
  1406.     } else {
  1407.     if (prevview == EMPTY) {
  1408.         sprintf(tmpbuf, "Was empty %d turns ago.", age);
  1409.     } else if (prevview == UNSEEN) {
  1410.         sprintf(tmpbuf, "Terrain first seen %d turns ago.", age);
  1411.     } else {
  1412.         side2 = side_n(vside(prevview));
  1413.         u = vtype(prevview);
  1414.         if (side2 != side) {
  1415.         sprintf(tmpbuf, "Saw %s %s, %d turns ago.",
  1416.             (side2 == NULL ? "independent" :
  1417.              side_name(side2)),
  1418.             u_type_name(u), age);
  1419.         } else {
  1420.         sprintf(tmpbuf, "Was occupied by your %s %d turns ago.",
  1421.             u_type_name(u), age);
  1422.         }
  1423.     }
  1424.     }
  1425. #endif
  1426.  
  1427. void
  1428. hp_desc(buf, unit, label)
  1429. char *buf;
  1430. Unit *unit;
  1431. int label;
  1432. {
  1433.     if (label) {
  1434.     sprintf(buf, "HP ");
  1435.     } else {
  1436.     buf[0] = '\0';
  1437.     }
  1438.     /* (print '-' or some such for zero hp case?) */
  1439.     if (unit->hp == u_hp(unit->type)) {
  1440.     tprintf(buf, "%d", unit->hp);
  1441.     } else {
  1442.     tprintf(buf, "%d/%d", unit->hp, u_hp(unit->type));
  1443.     } 
  1444. }
  1445.  
  1446. void
  1447. acp_desc(buf, unit, label)
  1448. char *buf;
  1449. Unit *unit;
  1450. int label;
  1451. {
  1452.     int u = unit->type;
  1453.  
  1454.     if (!completed(unit)) {
  1455.     sprintf(buf, "%d/%d done", unit->cp, u_cp(u));
  1456.     } else if (unit->act && u_acp(u) > 0) {
  1457.         if (label) {
  1458.             strcpy(buf, "ACP ");
  1459.         } else {
  1460.         buf[0] = '\0';
  1461.         }
  1462.     if (unit->act->acp == unit->act->initacp) {
  1463.         tprintf(buf, "%d", unit->act->acp);
  1464.     } else {
  1465.         tprintf(buf, "%d/%d", unit->act->acp, unit->act->initacp);
  1466.     }
  1467.     } else {
  1468.     buf[0] = '\0';
  1469.     }
  1470. }
  1471.  
  1472. int
  1473. supply_desc(buf, unit, mrow)
  1474. char *buf;
  1475. Unit *unit;
  1476. int mrow;
  1477. {
  1478.     int u = unit->type, m, mm, tmprow;
  1479.  
  1480.     tmprow = 0;
  1481.     buf[0] = '\0';
  1482.     mm = 0;
  1483.     for_all_material_types(m) {
  1484.     if (um_storage_x(u, m) > 0) {
  1485.         if (mm > 0 && mm % 3 == 0)
  1486.           ++tmprow;
  1487.         if (tmprow == mrow) {
  1488.         tprintf(buf, "%s %d/%d  ",
  1489.             m_type_name(m), unit->supply[m], um_storage_x(u, m));
  1490.         }
  1491.         ++mm;
  1492.     }
  1493.     }
  1494.     return (strlen(buf) > 0);
  1495. }
  1496.  
  1497. void
  1498. location_desc(buf, side, unit, u, x, y)
  1499. char *buf;
  1500. Side *side;
  1501. Unit *unit;
  1502. int u, x, y;
  1503. {
  1504.     int t = terrain_at(x, y);
  1505.     char *featurename;
  1506.  
  1507.     if (unit != NULL && unit->transport != NULL) {
  1508.     sprintf(buf, "In %s", short_unit_handle(unit->transport));
  1509.     } else if (unit != NULL || u != NONUTYPE) {
  1510.     sprintf(buf, "In %s", t_type_name(t));
  1511.     } else if (terrain_visible(side, x, y)) {
  1512.     sprintf(buf, "Empty %s", t_type_name(t));
  1513.     } else {
  1514.     sprintf(buf, "Unknown");
  1515.     }
  1516.     if (terrain_visible(side, x, y)) {
  1517.     linear_desc(buf, x, y);
  1518.     /* (should use feature desc?) */
  1519.     featurename = feature_name_at(x, y);
  1520.     if (!empty_string(featurename))
  1521.       tprintf(buf, " (%s)", featurename);
  1522.     if (temperatures_defined())
  1523.       tprintf(buf, " (T %d)", temperature_at(x, y));
  1524.     if (elevations_defined())
  1525.       tprintf(buf, " (El %d)", elev_at(x, y));
  1526.     /* (should list local weather also) */
  1527.     }
  1528.     tprintf(buf, " at %d,%d", x, y);
  1529. }
  1530.  
  1531. void
  1532. plan_desc(buf, unit)
  1533. char *buf;
  1534. Unit *unit;
  1535. {
  1536.     int i;
  1537.     Plan *plan = unit->plan;
  1538.     Task *task = plan->tasks;
  1539.  
  1540.     if (plan == NULL) {
  1541.     buf[0] = '\0';
  1542.         return;
  1543.     }
  1544.     sprintf(buf, "%s", plantypenames[plan->type]);
  1545.     if (plan->waitingfortasks)
  1546.       strcat(buf, " Wait");
  1547.     if (plan->asleep)
  1548.       strcat(buf, " Asleep");
  1549.     if (plan->reserve)
  1550.       strcat(buf, " Reserve");
  1551.     if (plan->delayed)
  1552.       strcat(buf, " Delay");
  1553.     if (plan->aicontrol)
  1554.       strcat(buf, " AI");
  1555.     if (plan->supply_is_low)
  1556.       strcat(buf, " Low");
  1557.     if (plan->supply_alarm)
  1558.       strcat(buf, " SupplyAlarm");
  1559.     if (plan->maingoal)
  1560.       strcat(buf, " <goal>");
  1561.     if (plan->formation)
  1562.       strcat(buf, " <formation>");
  1563.     if (plan->tasks) {
  1564.     i = 0;
  1565.     for_all_tasks(plan, task)
  1566.       ++i;
  1567.     tprintf(buf, " %d task", i);
  1568.     if (i > 1)
  1569.       strcat(buf, "s");
  1570.     } 
  1571. }
  1572.  
  1573. void
  1574. task_desc(buf, task)
  1575. char *buf;
  1576. Task *task;
  1577. {
  1578.     int i, slen;
  1579.     char *argtypes;
  1580.  
  1581.     sprintf(buf, "%s", taskdefns[task->type].name);
  1582.     switch (task->type) {
  1583.       case TASK_BUILD:
  1584.     tprintf(buf, " %s", u_type_name(task->args[0]));
  1585.     if (task->args[1] != 0) {
  1586.         Unit *unit = find_unit(task->args[1]);
  1587.  
  1588.         if (unit != NULL) {
  1589.         tprintf(buf, " (%d cp)", unit->cp);
  1590.         }
  1591.     }
  1592.     tprintf(buf, ", %d of %d", task->args[2], task->args[3]);
  1593.     break;
  1594.       case TASK_HIT_POSITION:
  1595.           tprintf(buf, " %d,%d", task->args[0], task->args[1]);
  1596.     break;
  1597.       case TASK_HIT_UNIT:
  1598.           tprintf(buf, " at %d,%d (type %d side %d)",
  1599.               task->args[0], task->args[1], task->args[2], task->args[3]);
  1600.     break;
  1601.       case TASK_MOVE_TO:
  1602.         if (task->args[2] == 0) {
  1603.         tprintf(buf, " %d,%d", task->args[0], task->args[1]);
  1604.         } else if (task->args[2] == 1) {
  1605.         tprintf(buf, " adjacent %d,%d", task->args[0], task->args[1]);
  1606.         } else {
  1607.         tprintf(buf, " within %d of %d,%d",
  1608.             task->args[2], task->args[0], task->args[1]);
  1609.         }
  1610.     break;
  1611.       default:
  1612.     argtypes = taskdefns[task->type].argtypes;
  1613.     slen = strlen(argtypes);
  1614.     for (i = 0; i < slen; ++i) {
  1615.         tprintf(buf, "%c%d", (i == 0 ? ' ' : ','), task->args[i]);
  1616.     }
  1617.     break;
  1618.     }
  1619.     tprintf(buf, " x %d", task->execnum);
  1620.     if (task->retrynum > 0) {
  1621.     tprintf(buf, " fail %d", task->retrynum);
  1622.     }
  1623. }
  1624.  
  1625. /* Format a clock time into a standard form.  This routine will omit the hours
  1626.    part if it will be uninteresting. */
  1627.  
  1628. void
  1629. time_desc(buf, time, maxtime)
  1630. char *buf;
  1631. int time, maxtime;
  1632. {
  1633.     int hour, minute, second;
  1634.  
  1635.     if (time >= 0) {
  1636.     hour = time / 3600;  minute = (time / 60) % 60;  second = time % 60;
  1637.         if (between(1, maxtime, 3600) && hour == 0) {
  1638.         sprintf(buf, "%.2d:%.2d", minute, second);
  1639.     } else {
  1640.         sprintf(buf, "%.2d:%.2d:%.2d", hour, minute, second);
  1641.     }
  1642.     } else {
  1643.         sprintf(buf, "??:??:??");
  1644.     }
  1645. }
  1646.  
  1647. /* General-purpose routine to take an array of anonymous unit types and
  1648.    summarize what's in it, using numbers and unit chars. */
  1649.  
  1650. char *
  1651. summarize_units(buf, ucnts)
  1652. char *buf;
  1653. int *ucnts;
  1654. {
  1655.     char tmp[BUFSIZE];  /* should be bigger? */
  1656.     int u;
  1657.  
  1658.     buf[0] = '\0';
  1659.     for_all_unit_types(u) {
  1660.     if (ucnts[u] > 0) {
  1661.         sprintf(tmp, " %d %s", ucnts[u], utype_name_n(u, 3));
  1662.         strcat(buf, tmp);
  1663.     }
  1664.     }
  1665.     return buf;
  1666. }
  1667.  
  1668. /* Compose a one-line comment on the game. */
  1669.  
  1670. char *
  1671. exit_commentary(side)
  1672. Side *side;
  1673. {
  1674.     int numingame = 0, numwon = 0, numlost = 0;
  1675.     Side *side2, *lastside, *winner, *loser;
  1676.  
  1677.     for_all_sides(side2) {
  1678.     if (side2->ingame) {
  1679.         ++numingame;
  1680.         lastside = side2;
  1681.     }
  1682.     if (side_won(side2)) {
  1683.         ++numwon;
  1684.         winner = side2;
  1685.     }
  1686.     if (side_lost(side2)) {
  1687.         ++numlost;
  1688.         loser = side2;
  1689.     }
  1690.     }
  1691.     if (numingame > 0) {
  1692.         if (0 /* could have been resolved, need to check scorekeepers */) {
  1693.         sprintf(spbuf, "The outcome remains undecided");
  1694. #ifdef RUDE
  1695.         if (numsides > 1) {
  1696.         strcat(spbuf, ", but you're probably the loser!");
  1697.         } else {
  1698.         strcat(spbuf, "...");
  1699.         }
  1700. #else
  1701.         strcat(spbuf, "...");
  1702. #endif /* RUDE */
  1703.     } else {
  1704.         sprintf(spbuf, "Game is over.");
  1705.         }
  1706.     } else if (numwon == numsides) {
  1707.     sprintf(spbuf, "Everybody won!");
  1708.     } else if (numlost == numsides) {
  1709.     sprintf(spbuf, "Everybody lost!");
  1710.     } else if (numwon == 1) {
  1711.     sprintf(spbuf, "%s won!", (side == winner ? "You" : side_desig(winner)));
  1712.     } else if (numlost == 1) {
  1713.     sprintf(spbuf, "%s lost!", (side == loser ? "You" : side_desig(loser)));
  1714.     } else {
  1715.     sprintf(spbuf, "Some won and some lost.");
  1716.     }
  1717.     return spbuf;
  1718. }
  1719.  
  1720. void
  1721. notify_all_of_resignation(side, side2)
  1722. Side *side, *side2;
  1723. {
  1724.     Side *side3;
  1725.  
  1726.     for_all_sides(side3) {
  1727.     if (side3 != side) {
  1728.         notify(side3,
  1729.            "%s %s giving up!",
  1730.            short_side_title_with_adjective(side,
  1731. #ifdef RUDE
  1732.            (flip_coin() ? "cowardly" : "wimpy")
  1733. #else
  1734.            NULL
  1735. #endif /* RUDE */
  1736.            ),
  1737.            (short_side_title_plural_p(side) ? "are" : "is"));
  1738.         if (side2 != NULL) {
  1739.         notify(side3, "... and donating everything to %s!",
  1740.                short_side_title(side2));
  1741.         }
  1742.     }
  1743.     }
  1744. }
  1745.  
  1746. /* The following routines should probably go into some kind of international
  1747.    type interface. */
  1748.  
  1749. /* Given a number, figure out what suffix should go with it. */
  1750.  
  1751. char *
  1752. ordinal_suffix(n)
  1753. int n;
  1754. {
  1755.     if (n % 100 == 11 || n % 100 == 12 || n % 100 == 13) {
  1756.     return "th";
  1757.     } else {
  1758.     switch (n % 10) {
  1759.       case 1:   return "st";
  1760.       case 2:   return "nd";
  1761.       case 3:   return "rd";
  1762.       default:  return "th";
  1763.     }
  1764.     }
  1765. }
  1766.  
  1767. /* Pluralize a word, attempting to be smart about various possibilities
  1768.    that don't have a different plural form (such as "Chinese" and "Swiss"). */
  1769.  
  1770. /* There should probably be a test for when to add "es" instead of "s". */
  1771.  
  1772. char *
  1773. plural_form(word)
  1774. char *word;
  1775. {
  1776.     char endch = ' ', nextend = ' ';
  1777.  
  1778.     if (word == NULL) {
  1779.     run_warning("plural_form given NULL string");
  1780.     pluralbuf[0] = '\0';
  1781.     return pluralbuf;
  1782.     }
  1783.     if (strlen(word) > 0)
  1784.       endch   = word[strlen(word)-1];
  1785.     if (strlen(word) > 1)
  1786.       nextend = word[strlen(word)-2];
  1787.     if (endch == 'h' || endch == 's' || (endch == 'e' && nextend == 's')) {
  1788.     sprintf(pluralbuf, "%s", word);
  1789.     } else {
  1790.     sprintf(pluralbuf, "%ss", word);
  1791.     }
  1792.     return pluralbuf;
  1793. }
  1794.  
  1795. /* General text generation. */
  1796.  
  1797. char *
  1798. make_text(buf, maker, a1, a2, a3, a4)
  1799. char *buf;
  1800. Obj *maker;
  1801. long a1, a2, a3, a4;
  1802. {
  1803.     if (buf == NULL)
  1804.       buf = xmalloc(BUFSIZE);
  1805.     if (maker != lispnil) {
  1806.     } else {
  1807.     sprintf(buf, "%ld %ld %ld %ld", a1, a2, a3, a4);
  1808.     }
  1809.     return buf;
  1810. }
  1811.  
  1812. /* Date/time parsing and formatting. */
  1813.  
  1814. long
  1815. parse_date(str)
  1816. char *str;
  1817. {
  1818.     return 1;
  1819. }
  1820.  
  1821. /* Compose a readable form of the given date. */
  1822.  
  1823. char *
  1824. absolute_date_string(date)
  1825. int date;
  1826. {
  1827.     /* The first time we ask for a date, interpret the calendar. */
  1828.     if (calendar_type == cal_unknown)
  1829.       init_calendar();
  1830.     switch (calendar_type) {
  1831.       case cal_turn:
  1832.     sprintf(datebuf, "%s%4ld", turnname, date);
  1833.     return datebuf;
  1834.       case cal_usual:
  1835.     return usual_date_string(date);
  1836.       default:
  1837.     case_panic("calendar type", calendar_type);
  1838.     }
  1839.     return "!?!";
  1840. }
  1841.  
  1842. /* Interpret the calendar definition. */
  1843.  
  1844. static void
  1845. init_calendar()
  1846. {
  1847.     int caltypeunknown = FALSE;
  1848.     char *stepnamestr;
  1849.     Obj *cal, *caltype, *stepname, *step;
  1850.  
  1851.     cal = g_calendar();
  1852.     /* Turn-based calendar is the default. */
  1853.     calendar_type = cal_turn;
  1854.     turnname = "Turn";
  1855.     if (cal == lispnil) {
  1856.     /* Default is fine. */
  1857.     } else if (consp(cal)) {
  1858.     caltype = car(cal);
  1859.     if ((symbolp(caltype) || stringp(caltype))
  1860.          && strcmp("usual", c_string(caltype)) == 0) {
  1861.         calendar_type = cal_usual;
  1862.         stepname = cadr(cal);
  1863.         if (symbolp(stepname) || stringp(stepname)) {
  1864.         stepnamestr = c_string(stepname);
  1865.             if (strcmp(stepnamestr, "second") == 0) {
  1866.             datesteptype = ds_second;
  1867.             } else if (strcmp(stepnamestr, "minute") == 0) {
  1868.             datesteptype = ds_minute;
  1869.             } else if (strcmp(stepnamestr, "hour") == 0) {
  1870.             datesteptype = ds_hour;
  1871.             } else if (strcmp(stepnamestr, "day") == 0) {
  1872.             datesteptype = ds_day;
  1873.             } else if (strcmp(stepnamestr, "week") == 0) {
  1874.             datesteptype = ds_week;
  1875.             } else if (strcmp(stepnamestr, "month") == 0) {
  1876.             datesteptype = ds_month;
  1877.             } else if (strcmp(stepnamestr, "season") == 0) {
  1878.             datesteptype = ds_season;
  1879.             } else if (strcmp(stepnamestr, "year") == 0) {
  1880.             datesteptype = ds_year;
  1881.             } else {
  1882.             init_warning("\"%s\" not a known date step name", stepnamestr);
  1883.             }
  1884.         } else {
  1885.         init_warning("No name for date step type, substituting `day'");
  1886.         datesteptype = ds_day;
  1887.         }
  1888.         /* Collect an optional multiple. */
  1889.         step = car(cddr(cal));
  1890.         datestep = (numberp(step) ? c_number(step) : 1);
  1891.         if (!empty_string(g_initial_date())) {
  1892.         set_initial_date(g_initial_date());
  1893.         }
  1894.     } else {
  1895.         caltypeunknown = TRUE;
  1896.     }
  1897.     } else if (stringp(cal)) {
  1898.     turnname = c_string(cal);
  1899.     } else {
  1900.     caltypeunknown = TRUE;
  1901.     }
  1902.     if (caltypeunknown)
  1903.       init_warning("Unknown calendar type");
  1904. }
  1905.  
  1906. /* Compose a date, omitting components supplied by the base date. */
  1907.  
  1908. char *
  1909. relative_date_string(date, base)
  1910. int date, base;
  1911. {
  1912.     /* should do this for real eventually */
  1913.     sprintf(datebuf, "%ld(%ld)", date, base);
  1914.     return datebuf;
  1915. }
  1916.  
  1917. extern int turns_between PARAMS ((char *str1, char *str2));
  1918.  
  1919. int
  1920. turns_between(datestr1, datestr2)
  1921. char *datestr1, *datestr2;
  1922. {
  1923.     int rslt;
  1924.     UsualDate date1, date2;
  1925.  
  1926.     if (calendar_type == cal_unknown)
  1927.       init_calendar();
  1928.     switch (calendar_type) {
  1929.       case cal_usual:
  1930.     parse_usual_date(datestr1, &date1);
  1931.     parse_usual_date(datestr2, &date2);
  1932.     rslt = date2.year - date1.year;
  1933.     if (datesteptype == ds_year)
  1934.       return (rslt / datestep);
  1935.     if (datesteptype < ds_year) {
  1936.         rslt = (12 * rslt) - (date2.month - date1.month);
  1937.     }
  1938.     if (datesteptype == ds_month)
  1939.       return (rslt / datestep);
  1940.     if (datesteptype < ds_month) {
  1941.         rslt = (30 * rslt) - (date2.day - date1.day);
  1942.     }
  1943.     if (datesteptype == ds_week)
  1944.       return (((rslt + 6) / 7) / datestep);
  1945.     if (datesteptype == ds_day)
  1946.       return (rslt / datestep);
  1947.     if (datesteptype < ds_day) {
  1948.         rslt = (24 * rslt) - (date2.hour - date1.hour);
  1949.     }
  1950.     if (datesteptype == ds_hour)
  1951.       return (rslt / datestep);
  1952.     return (rslt / datestep); /* semi-bogus */
  1953.       default:
  1954.     return 1;
  1955.     }
  1956. }
  1957.  
  1958. void
  1959. set_initial_date(str)
  1960. char *str;
  1961. {
  1962.     if (calendar_type == cal_unknown)
  1963.       init_calendar();
  1964.     switch (calendar_type) {
  1965.       case cal_usual:
  1966.     if (usual_initial == NULL)
  1967.       usual_initial = (UsualDate *) xmalloc(sizeof(UsualDate));
  1968.     parse_usual_date(str, usual_initial);
  1969.     break;
  1970.       default:
  1971.     break;
  1972.     }
  1973. }
  1974.  
  1975. static void
  1976. parse_usual_date(datestr, udate)
  1977. char *datestr;
  1978. UsualDate *udate;
  1979. {
  1980.     char monthname[BUFSIZE];
  1981.     int i, cnt;
  1982.     
  1983.     udate->second = udate->minute = udate->hour = udate->day = udate->month = udate->year = 0;
  1984.     monthname[0] = '\0';
  1985.     if (!empty_string(datestr)) {
  1986.     /* Assume it's in a standard date format. */
  1987.     switch (datesteptype) {
  1988.       case ds_hour:
  1989.         cnt = sscanf(datestr, "%d:00 %d %s %d",
  1990.              &(udate->hour), &(udate->day), monthname,
  1991.              &(udate->year));
  1992.         if (cnt != 4)
  1993.           cnt = sscanf(datestr, "%d %d %s %d",
  1994.                &(udate->hour), &(udate->day), monthname,
  1995.                &(udate->year));
  1996.         if (cnt != 4)
  1997.           goto bad_format;
  1998.         --(udate->day);
  1999.         break;
  2000.       case ds_day:
  2001.         cnt = sscanf(datestr, "%d %s %d", &(udate->day), monthname,
  2002.              &(udate->year));
  2003.         if (cnt != 3)
  2004.           goto bad_format;
  2005.         --(udate->day);
  2006.         break;
  2007.       case ds_week:
  2008.         cnt = sscanf(datestr, "%d %s %d", &(udate->day), monthname,
  2009.              &(udate->year));
  2010.         if (cnt != 3)
  2011.           goto bad_format;
  2012.         --(udate->day);
  2013.         break;
  2014.       case ds_month:
  2015.         cnt = sscanf(datestr, "%s %d", monthname, &(udate->year));
  2016.         if (cnt != 2)
  2017.           goto bad_format;
  2018.         break;
  2019.       case ds_year:
  2020.         cnt = sscanf(datestr, "%d", &(udate->year));
  2021.         if (cnt != 2)
  2022.           goto bad_format;
  2023.         return;
  2024.       default:
  2025.         init_warning("%d not a known date step type", datesteptype);
  2026.         break;
  2027.     }
  2028.     for (i = 0; i < 12; ++i) {
  2029.         if (strcmp(monthname, months[i]) == 0) {
  2030.         udate->month = i;
  2031.         return;
  2032.         }
  2033.     }
  2034.     init_warning("\"%s\" not a recognized month name", monthname);
  2035.     }
  2036.     return;
  2037.   bad_format:
  2038.     init_warning("\"%s\" is a badly formatted date", datestr);
  2039. }
  2040.  
  2041.  
  2042. /* Given a numeric data, convert it into something understandable.  Depends
  2043.    on the length of a turn. */
  2044.  
  2045. static char *
  2046. usual_date_string(date)
  2047. int date;
  2048. {
  2049.     int year = 0, season = 0, month = 0, day = 0;
  2050.     int hour = 0, second = 0, minute = 0;
  2051.  
  2052.     /* The date, which is a turn number, should be 1 or more, but this
  2053.        routine may be called before the game really starts, so return
  2054.        something that will be distinctive if it's ever displayed. */
  2055.     if (date <= 0)
  2056.       return "pregame";
  2057.     /* First displayed date is normally turn 1; be zero-based for
  2058.        the benefit of calculation. */
  2059.     --date;
  2060.     date *= datestep;
  2061.     if (usual_initial == NULL) {
  2062.     usual_initial = (UsualDate *) xmalloc(sizeof(UsualDate));
  2063.     if (!empty_string(g_initial_date()))
  2064.       parse_usual_date(g_initial_date(), usual_initial);
  2065.     }
  2066.     switch (datesteptype) {
  2067.       case ds_second:
  2068.     second = date % 60;
  2069.     minute = date / 60;
  2070.     sprintf(datebuf, "%d:%d", minute, second);
  2071.     break;
  2072.       case ds_minute:
  2073.     minute = date % 60;
  2074.     hour = date / 60 + usual_initial->hour;
  2075.     sprintf(datebuf, "%d:%d", hour, minute);
  2076.     break;
  2077.       case ds_hour:
  2078.      hour = (date + usual_initial->hour) % 24;
  2079.     /* Convert to days, then proceed as for days. */
  2080.      date = (date + usual_initial->hour) / 24;
  2081.     date += 30 * usual_initial->month;
  2082.     day = date % 365;
  2083.     month = day / 30;
  2084.     day = (day + usual_initial->day) % 30 + 1;
  2085.     year = date / 365 + usual_initial->year;
  2086.     sprintf(datebuf, "%d:00 %2d %s %d", hour, day, months[month], ABS(year));
  2087.     break;
  2088.       case ds_day:
  2089.     /* Should do this more accurately... */
  2090.     date += 30 * usual_initial->month;
  2091.     day = date % 365;
  2092.     month = day / 30;
  2093.     day = (day + usual_initial->day) % 30 + 1;
  2094.     year = date / 365 + usual_initial->year;
  2095.     sprintf(datebuf, "%2d %s %d", day, months[month], ABS(year));
  2096.     break;
  2097.       case ds_week:
  2098.     /* Convert to days, then proceed as for days. */
  2099.     date *= 7;
  2100.     date += 30 * usual_initial->month;
  2101.     day = date % 365;
  2102.     month = day / 30;
  2103.     day = (day + usual_initial->day) % 30 + 1;
  2104.     year = date / 365 + usual_initial->year;
  2105.     sprintf(datebuf, "%2d %s %d", day, months[month], ABS(year));
  2106.     break;
  2107.       case ds_month:
  2108.     date += usual_initial->month;
  2109.         month = date % 12;
  2110.     year = date / 12 + usual_initial->year;
  2111.     sprintf(datebuf, "%s %d", months[month], ABS(year));
  2112.     break;
  2113.       case ds_season:
  2114.          season = date % 4;
  2115.     year = date / 4 + usual_initial->year;
  2116.     sprintf(datebuf, "%d %d", season, ABS(year));
  2117.     break;
  2118.       case ds_year:
  2119.     year = date + usual_initial->year;
  2120.     sprintf(datebuf, "%d", ABS(year));
  2121.     break;
  2122.       default:
  2123.     sprintf(datebuf, "?what's a %d?", datesteptype);
  2124.     break;
  2125.     }
  2126.     if (year < 0) {
  2127.     strcat(datebuf, " BC");
  2128.     }
  2129.     return datebuf;
  2130. }
  2131.  
  2132. /* Show some overall numbers on performance of a side. */
  2133.  
  2134. void
  2135. write_side_results(fp, side)
  2136. FILE *fp;
  2137. Side *side;
  2138. {
  2139.     int i;
  2140.  
  2141.     if (side == NULL) {
  2142.     fprintf(fp, "Results for game as a whole:\n\n");
  2143.     } else {
  2144.     fprintf(fp, "Results for %s%s",
  2145.         short_side_title(side),
  2146.         (side_won(side) ? " (WINNER)" :
  2147.          (side_lost(side) ? " (LOSER)" :
  2148.           "")));
  2149.     for (i = 0; i < numscores; ++i) {
  2150.         fprintf(fp, " %d", side->scores[i]);
  2151.     }
  2152.     fprintf(fp, ", played by %s:\n\n",
  2153.         long_player_title(spbuf, side->player, NULL));
  2154.     }
  2155. }
  2156.  
  2157. /* Display what is essentially a double-column bookkeeping of unit gains
  2158.    and losses. */
  2159.  
  2160. void
  2161. write_unit_record(fp, side)
  2162. FILE *fp;
  2163. Side *side;
  2164. {
  2165.     int u, gainreason, lossreason, totgain, totloss, val;
  2166.  
  2167.     fprintf(fp, "Unit Record (gains and losses by cause and unit type)\n");
  2168.     fprintf(fp, " Unit Type ");
  2169.     for (gainreason = 0; gainreason < num_gain_reasons; ++gainreason) {
  2170.     fprintf(fp, " %3s", gain_reason_names[gainreason]);
  2171.     }
  2172.     fprintf(fp, " Gain |");
  2173.     for (lossreason = 0; lossreason < num_loss_reasons; ++lossreason) {
  2174.     fprintf(fp, " %3s", loss_reason_names[lossreason]);
  2175.     }
  2176.     fprintf(fp, " Loss |");
  2177.     fprintf(fp, " Total\n");
  2178.     for_all_unit_types(u) {
  2179.     if (u_possible[u]) {
  2180.         totgain = 0;
  2181.         fprintf(fp, " %9s ", utype_name_n(u, 9));
  2182.         for (gainreason = 0; gainreason < num_gain_reasons; ++gainreason) {
  2183.         val = gain_count(side, u, gainreason);
  2184.         if (val > 0) {
  2185.             fprintf(fp, " %3d", val);
  2186.             totgain += val;
  2187.         } else {
  2188.             fprintf(fp, "    ");
  2189.         }
  2190.         }
  2191.         fprintf(fp, "  %3d |", totgain);
  2192.         totloss = 0;
  2193.         for (lossreason = 0; lossreason < num_loss_reasons; ++lossreason) {
  2194.         val = loss_count(side, u, lossreason);
  2195.         if (val > 0) {
  2196.             fprintf(fp, " %3d", val);
  2197.             totloss += val;
  2198.         } else {
  2199.             fprintf(fp, "    ");
  2200.         }
  2201.         }
  2202.         fprintf(fp, "  %3d |", totloss);
  2203.         fprintf(fp, "  %3d\n", totgain - totloss);
  2204.     }
  2205.     }
  2206.     fprintf(fp, "\n");
  2207. }
  2208.  
  2209. static int
  2210. gain_count(side, u, r)
  2211. Side *side;
  2212. int u, r;
  2213. {
  2214.     int sum;
  2215.  
  2216.     if (side != NULL)
  2217.       return side_gain_count(side, u, r);
  2218.     sum = 0;
  2219.     for_all_sides(side) {
  2220.     sum += side_gain_count(side, u, r);
  2221.     }
  2222.     return sum;
  2223. }
  2224.  
  2225. static int
  2226. loss_count(side, u, r)
  2227. Side *side;
  2228. int u, r;
  2229. {
  2230.     int sum;
  2231.  
  2232.     if (side != NULL)
  2233.       return side_loss_count(side, u, r);
  2234.     sum = 0;
  2235.     for_all_sides(side) {
  2236.     sum += side_loss_count(side, u, r);
  2237.     }
  2238.     return sum;
  2239. }
  2240.  
  2241. /* Nearly-raw combat statistics; hard to interpret, but they provide
  2242.    a useful check against subjective evaluation of performance. */
  2243.  
  2244. void
  2245. write_combat_results(fp, side)
  2246. FILE *fp;
  2247. Side *side;
  2248. {
  2249.     int a, d, atk;
  2250.  
  2251.     fprintf(fp,
  2252.         "Unit Combat Results (average damage over # attacks against enemy, by type)\n");
  2253.     fprintf(fp, " A  D->");
  2254.     for_all_unit_types(d) {
  2255.     if (u_possible[d]) {
  2256.         fprintf(fp, " %4s ", utype_name_n(d, 4));
  2257.     }
  2258.     }
  2259.     fprintf(fp, "\n");
  2260.     for_all_unit_types(a) {
  2261.     if (u_possible[a]) {
  2262.         fprintf(fp, " %4s ", utype_name_n(a, 4));
  2263.         for_all_unit_types(d) {
  2264.         if (u_possible[d]) {
  2265.             atk = atkstats(side, a, d);
  2266.             if (atk > 0) {
  2267.             fprintf(fp, " %5.2f",
  2268.                 ((float) hitstats(side, a, d)) / atk);
  2269.             } else {
  2270.             fprintf(fp, "      ");
  2271.             }
  2272.         }
  2273.         }
  2274.         fprintf(fp, "\n     ");
  2275.         for_all_unit_types(d) {
  2276.         if (u_possible[d]) {
  2277.             atk = atkstats(side, a, d);
  2278.             if (atk > 0) {
  2279.             fprintf(fp, " %4d ", atk);
  2280.             } else {
  2281.             fprintf(fp, "      ");
  2282.             }
  2283.         }
  2284.         }
  2285.         fprintf(fp, "\n");
  2286.     }
  2287.     }
  2288.     fprintf(fp, "\n");
  2289. }
  2290.  
  2291. static int
  2292. atkstats(side, a, d)
  2293. Side *side;
  2294. int a, d;
  2295. {
  2296.     int sum;
  2297.  
  2298.     if (side != NULL)
  2299.       return side_atkstats(side, a, d);
  2300.     sum = 0;
  2301.     for_all_sides(side) {
  2302.     sum += side_atkstats(side, a, d);
  2303.     }
  2304.     return sum;
  2305. }
  2306.  
  2307. static int
  2308. hitstats(side, a, d)
  2309. Side *side;
  2310. int a, d;
  2311. {
  2312.     int sum;
  2313.  
  2314.     if (side != NULL)
  2315.       return side_hitstats(side, a, d);
  2316.     sum = 0;
  2317.     for_all_sides(side) {
  2318.     sum += side_hitstats(side, a, d);
  2319.     }
  2320.     return sum;
  2321. }
  2322.  
  2323. void
  2324. dice_desc(buf, dice)
  2325. char *buf;
  2326. int dice;
  2327. {
  2328.     int numdice, die, offset;
  2329.  
  2330.     if (dice >> 14 == 0 || dice >> 14 == 3) {
  2331.     sprintf(buf, "%d", dice);
  2332.     } else {
  2333.         numdice = (dice >> 11) & 0x07;
  2334.         die = (dice >> 7) & 0x0f;
  2335.         offset = dice & 0x7f;
  2336.         if (offset == 0) {
  2337.         sprintf(buf, "%dd%d", numdice, die);
  2338.         } else {
  2339.         sprintf(buf, "%dd%d+%d", numdice, die, offset);
  2340.         }
  2341.     }
  2342. }
  2343.  
  2344. /* The following code formats a list of types that are missing images. */
  2345.  
  2346. void
  2347. record_missing_image(typtyp, str)
  2348. int typtyp;
  2349. char *str;
  2350. {
  2351.     if (missinglist == NULL) {
  2352.     missinglist = xmalloc(BUFSIZE);
  2353.     missinglist[0] = '\0';
  2354.     }
  2355.     ++missing[typtyp];
  2356.     /* Add the name of the image-less type, but only if one of
  2357.        the first few. */
  2358.     if (between(1, totlisted, NUMTOLIST))
  2359.       strcat(missinglist, ",");
  2360.     if (totlisted < NUMTOLIST) {
  2361.     strcat(missinglist, str);
  2362.     } else if (totlisted == NUMTOLIST) {
  2363.     strcat(missinglist, "...");
  2364.     }
  2365.     ++totlisted;
  2366. }
  2367.  
  2368. /* Return true if any images could not be found, and provide some helpful info
  2369.    into the supplied buffer. */
  2370.  
  2371. int
  2372. missing_images(buf)
  2373. char *buf;
  2374. {
  2375.     if (missinglist == NULL)
  2376.       return FALSE;
  2377.     buf[0] = '\0';
  2378.     if (missing[UTYP] > 0)
  2379.       tprintf(buf, " %d unit images", missing[UTYP]);
  2380.     if (missing[TTYP] > 0)
  2381.       tprintf(buf, " %d terrain images", missing[TTYP]);
  2382.     if (missing[3] > 0)
  2383.       tprintf(buf, " %d emblems", missing[3]);
  2384.     tprintf(buf, " - %s", missinglist);
  2385.     return TRUE;
  2386. }
  2387.  
  2388.